//soc confreg
#define CONFREG_NULL            0xbfaf8ffc

#define CONFREG_CR0             0xbfaf8000
#define CONFREG_CR1             0xbfaf8004
#define CONFREG_CR2             0xbfaf8008
#define CONFREG_CR3             0xbfaf800c
#define CONFREG_CR4             0xbfaf8010
#define CONFREG_CR5             0xbfaf8014
#define CONFREG_CR6             0xbfaf8018
#define CONFREG_CR7             0xbfaf801c

#define IO_SIMU_ADDR            0xbfafffec
#define UART_ADDR               0xbfaffff0
#define SIMU_FLAG_ADDR          0xbfaffff4
#define OPEN_TRACE_ADDR         0xbfaffff8
#define NUM_MONITOR_ADDR        0xbfaffffc
#define LED_ADDR                0xbfaff000
#define LED_RG0_ADDR            0xbfaff004
#define LED_RG1_ADDR            0xbfaff008
#define NUM_ADDR                0xbfaff010
#define SWITCH_ADDR             0xbfaff020
#define BTN_KEY_ADDR            0xbfaff024
#define BTN_STEP_ADDR           0xbfaff028
#define SW_INTER_ADDR           0xbfaff02c //switch interleave
#define TIMER_ADDR              0xbfafe000

// ------- extra confreg ------
#define RAND_DATA_ADDR               0xbfaf8040
#define LED_DOT1_ADDR  0xbfaf8044    // 32'hbfaf_8044
#define LED_DOT2_ADDR  0xbfaf8048    // 32'hbfaf_8048
#define BUTTON_ADDR    0xbfaf804c    // 32'hbfaf_804c 

#define NAME_ADDR      0x00001000

// ----------------------------


// #define SNAKE_ADDR              0xbfca0004
// #define SNALE_LEN_ADDR          0xbfca0000

#define SNAKE_SIZE              16
#define SNAEK_SIZE_LOG          2 // p++ : p + 4

// ----- RETURE ADDR -----
#define G_ADDR             0x00010000 // 1 int
#define GET_SNAKE_ADDR     0x00010020 // 4 int
#define RAND_ADDR          0x00010040 // 1 int 
#define PRESSBUTTON_ADDR   0x00010044 // 1 int 
#define CHECK_OVER_ADDR    0x00010048 // 1 int

#define display_number_addr 0xbfaf2018
#define display_vaild_addr  0xbfaf2024
#define display_value_addr  0xbfaf2020
#define display_name_addr   0xbfaf2000

// -----------------------


// ----- GLOBAL VAR ADDR ----
#define SNAKE_LEN_ADDR  0x00000000
#define SNAKE_DIR_ADDR  0x00000004
#define FOOD_X_ADDR     0x00000008
#define FOOD_Y_ADDR     0x0000000c
#define RANK_LEN_ADDR   0x00000010

#define SNAKE_ADDR      0x00000200
#define SNAKE_MAP_ADDR  0x00000600


#define RANK_ADDR       0x00000800

// --------------------------

#define UP     1
#define DOWN   2
#define LEFT   3
#define RIGHT  4
/*
 * struct node
 * {
 *     int pre_x, pre_y, x, y;
 * };
*/


void delay(int x){
    int i , j;
    for(i = 0; i < x; i++) 
        for(j = 0; j < 1000; j++) 
        ;
}

void set_number(int x){
    int *p = NUM_ADDR;
    *p = x;
}

void set_led(int x){
    int *p = LED_ADDR;
    *p = x;
}

void breath_light()
{
    int* brightness;
    brightness = LED_ADDR;
    *brightness = 0xffff;    
    int second;
    int i, j, k;
    second = 0;

    int *numDisplay;
    numDisplay = NUM_ADDR;
    *numDisplay = 0x8888;
    int times = 0;
    while(times < 1){
            while(1){
                *numDisplay = 0x6666;
                second++;
                if(second == 60000-2){
                    break;
                }
                *brightness = 0x0000;
                for(i=0;i<second;i=i+1000);  
                *brightness = 0xffff;
                for(i=second;i<60000;i=i+1000); 
            }   

            while(1){
                *numDisplay = 0x2333;
                second--;
                if(second == 0){
                    break;
                }
                *brightness = 0x0000;
                for(i=0;i<second;i=i+1000);   
                *brightness = 0xffff;
                for(i=second;i<60000;i=i+1000);
            }  

            while(1){
                *numDisplay = 0x6666;
                second++;
                if(second == 60000-2){
                    break;
                }
                *brightness = 0x0000;
                for(i=0;i<second;i=i+1000);  
                *brightness = 0xffff;
                for(i=second;i<60000;i=i+1000); 
            }   

            while(1){
                *numDisplay = 0x2333;
                second--;
                if(second == 0){
                    break;
                }
                *brightness = 0x0000;
                for(i=0;i<second;i=i+1000);   
                *brightness = 0xffff;
                for(i=second;i<60000;i=i+1000);
            }   

            times++;       
    }
}

void flush(int rank, int data)    //update the lcd screen 22*2 
{
    int *display_number = display_number_addr;
    int *display_vaild = display_vaild_addr;
    int *display_value = display_value_addr;
    int *display_name = display_name_addr;
    int *p = 0xbfaff010;
    *display_number = (rank<<1)-1;
    *display_value = data;
    *display_name = rank;
        *p = rank; 
    *display_vaild = 1;
    int j ;
    for( j=0; j<1000; j++);
    *display_vaild = 0;
    // for(j=0;j<5000;j++);
}

void rand(){
    int *res = RAND_ADDR;
    int *p = RAND_DATA_ADDR ;
    *res = *p;
}

void pressButton(){
    int *p = BUTTON_ADDR;
    int button_value = *p;
    int status;
    int *q = PRESSBUTTON_ADDR;
    if(button_value == 0b1110){
        *q = 1;
    }
    else if(button_value == 0b1101){
        *q = 2;
    }
    else if(button_value == 0b1011){
        *q = 3;
    }
    else if(button_value == 0b0111){
        *q = 4;
    }
    else{
        *q = 5;
    }
}

void init(){
    int *p = SNAKE_LEN_ADDR;
    *p = 1;
    // p = SNAKE_DIR_ADDR;
    // *p = 1;
}

void set_snake(int idx, int pre_x, int pre_y, int x, int y){
    int *p = SNAKE_ADDR;
    p += idx << SNAEK_SIZE_LOG ;
    *p = pre_x; p++;
    *p = pre_y; p++;
    *p = x;     p++;
    *p = y;
}

void set_food(int x, int y){
    int *p = FOOD_X_ADDR;
    *p = x;
    p = FOOD_Y_ADDR;
    *p = y;
}

void get_snake(int idx){
    int *res = GET_SNAKE_ADDR;
    int *p = SNAKE_ADDR;
    p += idx << SNAEK_SIZE_LOG;
    *res = *p; res ++; p++;
    *res = *p; res ++; p++;
    *res = *p; res ++; p++;
    *res = *p;
}



void snake_move(int dir, int dx, int dy){

    int *p = SNAKE_DIR_ADDR;
    int snake_dir = *p;
    p = FOOD_X_ADDR;
    int food_x = *p;
    p = FOOD_Y_ADDR;
    int food_y = *p;
    int get_food = 0;
    int turn = 1;
    if(dir > 4) return ;
    p = SNAKE_LEN_ADDR;
    int len = *p;
    if(len > 1) {
        get_snake(0);
        p = GET_SNAKE_ADDR ;
        int head_x = *(p+2);
        int head_y = *(p+3);
        get_snake(1);
        p = GET_SNAKE_ADDR ;
        int x1 = *(p+2);
        int y1 = *(p+3);
        if(head_x + dx == x1 && head_y + dy == y1) return ; 
    }
    // if((snake_dir == 1 || snake_dir == 2) && (dir == 1 || dir == 2))
    //     return;
    // if((snake_dir == 3 || snake_dir == 4) && (dir == 3 || dir == 4))  
    //     return 
    // if(turn == 0) {
    //     if(snake_dir == 1) {
    //         dx = -1; dy = 0; 
    //     }
    //     else if(snake_dir == 2) {
    //         dx = 1; dy =0 ;
    //     }
    //     else if(snake_dir == 3) {
    //         dx = 0; dy = -1; 
    //     }
    //     else {
    //         dx = 0; dy = -1;
    //     }
    // }
    get_snake(0);
    p = GET_SNAKE_ADDR;
    int x = *(p+2);
    int y = *(p+3);
    int last_x = x , last_y = y;
    x += dx;
    y += dy;
    if(x == food_x && y == food_y) get_food = 1;
    set_snake(0, -1, -1, x, y);
    p = SNAKE_LEN_ADDR;
    len = *p;
    int i;
    for(i = 1; i < len; i++){
        get_snake(i);
        p = GET_SNAKE_ADDR;
        int pre_x = *p;
        int pre_y = *(p+1);
        int cur_x = *(p+2);
        int cur_y = *(p+3);
        set_snake(i, x, y, pre_x, pre_y);
        x = pre_x;
        y = pre_y;
        last_x = cur_x;
        last_y = cur_y;
    }

    get_snake(0);
    p = GET_SNAKE_ADDR;
    int head_x = *(p+2);
    int head_y = *(p+3);

    // get food
    if(get_food != 0) {
        // while (1) {
        //     set_led(~get_food);

        // }
        p = SNAKE_LEN_ADDR;
        *p = len+1;
        set_snake(len, x, y, last_x, last_y);

        int food_ok = 0;
        while(food_ok == 0){
            rand();
            p = RAND_ADDR;
            int t = *p;
            t = t & 0x0000003f;
            x = t >> 3;
            y = t & 0x00000007;
            if(x != head_x || y != head_y) 
                food_ok = 1;
        }
        p = FOOD_X_ADDR; *p = x;
        p = FOOD_Y_ADDR; *p = y;
    }
}

void check_over(){
    int *p = SNAKE_ADDR;
    int x = *(p+2);
    int y = *(p+3);
    int over = 0;
    if(x < 0 || x > 7)  over = 1;
    if(y < 0 || y > 7)  over = 1;
    p = SNAKE_LEN_ADDR;
    int len = *p;
    int i;
    for(i = 1; i < len; i++){
        get_snake(i);
        p = GET_SNAKE_ADDR;
        int cur_x = *(p+2); 
        int cur_y = *(p+3);
        if(x == cur_x && y == cur_y) 
            over = 1;
    }
    p =  CHECK_OVER_ADDR;
    *p = over;
}

void get_food(){

}

void show(){
    int *p = SNAKE_MAP_ADDR;
    int map_data1 = *p;
    int map_data2 = *(p+1);

    p = LED_DOT1_ADDR;
    *p = map_data1;
    p = LED_DOT2_ADDR;
    *p = map_data2;
}

void update_snake_map(){
    int map_data1 = 0;
    int map_data2 = 0;

    // int *p = SNAKE_MAP_ADDR;
    // *p = 0;
    int *p = SNAKE_LEN_ADDR;
    int len = *p;
    int i;
    for(i = 0; i < len; i++){
        get_snake(i);
        p = GET_SNAKE_ADDR;
        int x = *(p+2);
        int y = *(p+3);
        if(x < 4) {
            int t = (x<<3) + y;
            t = 31 - t;
            map_data1 |= 1 << t;
        }
        else {
            x -= 4;
            int t = (x<<3) + y;
            t = 31 - t;
            map_data2 |= 1 << t;
        }
    }
    p = SNAKE_MAP_ADDR;
    *p = map_data1;
    p++;
    *p = map_data2;
}

void update_food_map(){
    int *q = FOOD_X_ADDR;
    int x = *q;
    q = FOOD_Y_ADDR;
    int y = *q;
    int *p = SNAKE_MAP_ADDR;
    int map_data1 = *p;
    int map_data2 = *(p+1);
    if(x < 4) {
        int t = (x<<3) + y;
        t = 31 - t;
        map_data1 |= 1 << t;
        *p = map_data1;
    }
    else {
        x -= 4;
        int t = (x<<3) + y;
        t = 31 - t;
        map_data2 |= 1 << t;
        *(p+1) = map_data2;
    }
}

void debug(int x){
    int a = x;
}

void update_rank(int point){
    int *p = RANK_LEN_ADDR ;
    *p += 1;
    int rank_len = *p;
    p = RANK_ADDR;
    p += rank_len;
    *p = point;
    int i;
    for(i = 0; i < 1000; i++)
        flush(rank_len, point);
}

void init_name()
{
    int *p=NAME_ADDR;
*p=0x07e18181; *(p+1)=0x85878381;
p=p+2;
*p=0x0fc30303; *(p+1)=0x0b0f0702;
p=p+2;
*p=0x0f860606; *(p+1)=0x060e0e04;
p=p+2;
*p=0x0f1c1c1c; *(p+1)=0x1c1c1c18;
p=p+2;
*p=0x0e383838; *(p+1)=0x38383830;
p=p+2;
*p=0x0c606060; *(p+1)=0x60606060;
p=p+2;
*p=0x08c0c0c0; *(p+1)=0xc0c0c0f0;
p=p+2;
*p=0x01818181; *(p+1)=0x818181f1;
p=p+2;
*p=0x03030303; *(p+1)=0x030303e3;
p=p+2;
*p=0x08199999; *(p+1)=0x95a5a3c2;
p=p+2;
*p=0x0e7663c1; *(p+1)=0x81818183;
p=p+2;
*p=0x0e7666c7; *(p+1)=0x86c6663f;
p=p+2;
*p=0x07e7e181; *(p+1)=0x81818181;
p=p+2;
*p=0x07e3c181; *(p+1)=0x81878381;
p=p+2;
*p=0x03c66c38; *(p+1)=0x1cb663e1;
p=p+2;
*p=0x04444dfd; *(p+1)=0x5d544448;
p=p+2;
*p=0x08888bea; *(p+1)=0xaaa88880;
p=p+2;
*p=0x010107d5; *(p+1)=0x55510101;
p=p+2;
*p=0x02121fba; *(p+1)=0xbab21212;
p=p+2;
*p=0x04242f65; *(p+1)=0x65642424;
p=p+2;
*p=0x08484eda; *(p+1)=0xdad84848;
p=p+2;
*p=0x00808db5; *(p+1)=0xa5a08081;
p=p+2;
*p=0x01111b7b; *(p+1)=0x5b511112;
p=p+2;
*p=0x022226f6; *(p+1)=0xa6a22224;
p=p+2;
*p=0x04444dfd; *(p+1)=0x5d544448;
p=p+2;
*p=0x0f75555b; *(p+1)=0x705f597f;
p=p+2;
*p=0x0eeaaaa6; *(p+1)=0xe0aea2ee;
p=p+2;
*p=0x0dc5455d; *(p+1)=0xd15d45cd;
p=p+2;
*p=0x0b9a9abb; *(p+1)=0xb2ba9b9a;
p=p+2;
*p=0x07252577; *(p+1)=0x75752724;
p=p+2;
*p=0x0e4a4aee; *(p+1)=0xeaea4e48;
p=p+2;
*p=0x0c8485cd; *(p+1)=0xc5c48c80;
p=p+2;
*p=0x09192bfb; *(p+1)=0x8bf94941;
p=p+2;
*p=0x022247f7; *(p+1)=0x07f29292;
}

void name_delay()
{
    int i ;
    for( i=1;i<=1000000;i++);
}

void name_show()
{
    int *p = NAME_ADDR;
    int *d1 = LED_DOT1_ADDR;
    int *d2 = LED_DOT2_ADDR;
    while((*p)!=0)
    {
        *d1=*p;
        *d2=*(p+1);
        name_delay();
        p+=2;
    }
}

void _snake_main(){
// L1: 
        init();
        int x = 3, y = 3;
        int *p;
        set_snake(0, -1, -1, x, y);
        update_snake_map();
        int food_ok = 0;
        while(food_ok == 0){
            rand();
            int *p = RAND_ADDR;
            int t = *p;
            t = t & 0x0000003f;
            x = t >> 3;
            y = t & 0x00000007;
            if(x != 3 || y != 3) 
                food_ok = 1;
        }
        p = FOOD_X_ADDR;
        *p = x;
        p = FOOD_Y_ADDR ;
        *p = y;
        update_food_map();

        // int *q = SNAKE_LEN_ADDR;
        // *q = 2;
        // set_snake(1, 3, 3, 3, 4);
        int last_btn = 5;
        while (1)
        {
            rand();
            p = RAND_ADDR;
            int  t = *p;

            int dx, dy;
            // ----- input ----- 
            pressButton();
            p = PRESSBUTTON_ADDR;
            int btn_dir = *p;
            // p = NUM_ADDR;
            // *p = btn_dir;
            if(last_btn == 5 &&btn_dir == 1) {
                dx = -1;
                dy = 0;
                last_btn = btn_dir;
            }
            else if(last_btn == 5 && btn_dir == 2) {
                dx = 1;
                dy = 0;
                last_btn = btn_dir;
            }
            else if(last_btn == 5 && btn_dir == 3) {
                dx = 0;
                dy = -1;
                last_btn = btn_dir;
            }
            else if(last_btn == 5 && btn_dir == 4) {
                dx = 0;
                dy = 1;
                last_btn = btn_dir;
            }
            else {
                dx = 0; dy = 0;
                last_btn = btn_dir;
                btn_dir = 5;
            }
            // delay(1000);

            // ----- update -----
            snake_move(btn_dir, dx, dy);

            get_snake(0);
            p = GET_SNAKE_ADDR ;
            x = *(p+2);
            y = *(p+3);

            // x += dx;
            // y += dy;
            p = FOOD_X_ADDR;
            int tmp_x = *p;
            p = FOOD_Y_ADDR;
            int tmp_y = *p;
            // set_number(( x << 24 )| (y << 16 )| ( tmp_x << 8) | (tmp_y));
            // int tmp = ( x << 24 )| (y << 16 )| (dx << 8) | (dy);
            p = SNAKE_LEN_ADDR;
            int len = *p;
            int tmp = ( x << 24 )| (y << 16 ) | ( tmp_x << 8) | (tmp_y);
            p = NUM_ADDR;
            *p = tmp;
            int point;
            if      (len == 0 ) point = 0xffff;
            else if (len == 1 ) point = 0xfffe;
            else if (len == 2 ) point = 0xfffd;
            else if (len == 3 ) point = 0xfffb;
            else if (len == 4 ) point = 0xfff7;
            else if (len == 5 ) point = 0xffef;
            else if (len == 6 ) point = 0xffdf;
            else if (len == 7 ) point = 0xffbf;
            else if (len == 8 ) point = 0xff7f;
            else if (len == 9 ) point = 0xfeff;
            else if (len == 10) point = 0xfdff;
            else if (len == 11) point = 0xfbff;
            else if (len == 12) point = 0xf7ff;
            else if (len == 13) point = 0xefff;
            else if (len == 14) point = 0xdfff;
            else if (len == 15) point = 0xbfff;
            else                point = 0x7fff;
            // switch (len)
            // {
            //     case 0 : point = 0xffff; break ;
            //     case 1 : point = 0xfffe; break ;
            //     case 2 : point = 0xfffd; break ;
            //     case 3 : point = 0xfffb; break ;
            //     case 4 : point = 0xfff7; break ;
            //     case 5 : point = 0xffef; break ;
            //     case 6 : point = 0xffdf; break ;
            //     case 7 : point = 0xffbf; break ;
            //     case 8 : point = 0xff7f; break ;
            //     case 9 : point = 0xfeff; break ;
            //     case 10: point = 0xfdff; break ;
            //     case 11: point = 0xfbff; break ;
            //     case 12: point = 0xf7ff; break ;
            //     case 13: point = 0xefff; break ;
            //     case 14: point = 0xdfff; break ;
            //     case 15: point = 0xbfff; break ;
            //     case 16: point = 0x7fff; break ;
            //     default:
            //         point = 0x7fff;
            //         break;
            // }
            set_led(point);
            // int x = *(p+2);
            // int y = *(p+3);
            // x += dx ; y += dy;

            

            // set_snake(0, -1, -1, x, y);

            update_snake_map();
            p = FOOD_X_ADDR;
            int food_x = *p;
            p = FOOD_Y_ADDR;
            int food_y = *p;
            update_food_map(); 

            // ----- show ------
            show();

            // ----- over ------
            check_over();
            p = CHECK_OVER_ADDR;
            int over = *p;
            if(over){
                while (1)
                {
                    p = NUM_ADDR;
                    *p = 0x12345678;
                    breath_light();
                    // goto L1;
                    update_rank(len);
                    return ;
                }
                
            }

        }
}

void snake_main(){
    // while (1){
    //     flush(2, 123);
    // }
    init_name();
    name_show();
    int *p = RANK_LEN_ADDR;
    *p = 0;
    while (1)
    {
        _snake_main();
    }
    
    
}
